﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace JTActiveSafety.Gateway.Internal
{
    public class TcpChannel : IDisposable
    {
        /// <summary>
        /// Socket id
        /// </summary>
        public Guid SocketId { get; private set; } = Guid.NewGuid();
        public IPEndPoint RemoteEndPoint { get; protected set; }
        public Socket ClientSocket { get; protected set; }

        /// <summary>
        /// 最后活动(接收或发送数据成功为准，初始以建立连接为准)时间，相对于服务器启动/重启后的相对秒数
        /// </summary>
        //public long revTime { get; set; }
        public long LastActiveTime { get; private set; }

        //接收缓冲区
        protected byte[] Buffer = new byte[10240];


        //连接IP地址


        /// <summary>
        /// 获取远程连接的ip地址
        /// </summary>
        public string host
        {
            get
            {
                if (RemoteEndPoint != null)
                {
                    return RemoteEndPoint.Address.ToString();
                }
                else
                {
                    return ((IPEndPoint)ClientSocket.RemoteEndPoint).Address.ToString();
                }
            }
        }
        /// <summary>
        /// 获取远程连接的端口
        /// </summary>
        public int port
        {
            get
            {
                if (RemoteEndPoint != null)
                {
                    return RemoteEndPoint.Port;
                }
                else
                {
                    return ((IPEndPoint)ClientSocket.RemoteEndPoint).Port;
                }
            }
        }

        public TcpChannel(Socket cSocket)
        {
            this.ClientSocket = cSocket;
            SocketId = Guid.NewGuid();
            try
            {
                this.ClientSocket.BeginReceive(this.Buffer, 0, this.Buffer.Length, SocketFlags.None, new AsyncCallback(doReceive), this.ClientSocket);
            }
            catch
            {
                OnDisconnect();
            }
        }

        public TcpChannel(string host, int port)
        {
            SocketId = Guid.NewGuid();
            RemoteEndPoint = new IPEndPoint(IPAddress.Parse(host), port);
        }

        public virtual void Connect()
        {
            if (ClientSocket != null)
            {
                ClientSocket.Close();
                ClientSocket.Dispose();
                ClientSocket = null;
            }

            OnConnecting();
            try
            {
                this.ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                this.ClientSocket.SendBufferSize = 1024 * 32;
                this.ClientSocket.ReceiveBufferSize = 1024 * 32;
                this.ClientSocket.SendTimeout = 1000;

                LastActiveTime = (long)HostRuntime.TickSeconds;
                this.ClientSocket.BeginConnect(RemoteEndPoint, new AsyncCallback(doConnect), this.ClientSocket);
            }
            catch (Exception ex)
            {
                //onError("connect时发生错误", ex);
                OnConnected(false);
                return;
            }
        }


        /// <summary>
        /// 断开链接
        /// </summary>

        public virtual void Disconnect()
        {
            try
            {
                ClientSocket.Shutdown(SocketShutdown.Both);
            }
            catch { }
            finally
            {
                ClientSocket.Close();
                ClientSocket.Dispose();
                ClientSocket = null;
            }
        }

        public virtual int Send(byte[] bytes)
        {
            try
            {
                int r = this.ClientSocket.Send(bytes);
                if (r == 0)
                {
                    Disconnect();
                }
                return r;
            }
            catch
            {
                Disconnect();
                return 0;
            }
        }

        public virtual async void SendAsync(byte[] data, CancellationToken cancellationToken = default)
        {
            try
            {
                await ClientSocket.SendAsync(data, SocketFlags.None, cancellationToken);
            }
            catch
            {
                Disconnect();
            }
        }
 
        protected void doConnect(IAsyncResult ar)
        {
            try
            {
                Socket sock = ar.AsyncState as Socket;
                sock.EndConnect(ar);
                sock.BeginReceive(this.Buffer, 0, this.Buffer.Length, SocketFlags.None, new AsyncCallback(doReceive), sock);
                OnConnected(true);
            }
            catch (Exception ex)
            {
                //onError("Connected发生错误", ex);
                OnConnected(false);
                return;
            }
        }

        protected void doReceive(IAsyncResult ar)
        {
            int revLength = 0;
            Socket sock = ar.AsyncState as Socket;
            try
            {
                revLength = sock.EndReceive(ar);

                if (revLength > 0)
                {
                    byte[] b = new byte[revLength];
                    Array.Copy(Buffer, b, revLength);
                    onReceived(b);
                    LastActiveTime = (long)HostRuntime.TickSeconds;
                }
                else
                {
                    throw new Exception("socket EndReceive Size = 0 ");
                }

                sock.BeginReceive(this.Buffer, 0, this.Buffer.Length, SocketFlags.None, new AsyncCallback(doReceive), sock);
            }
            catch (Exception ex)
            {
                onError("Receive发生错误：", ex);
                OnDisconnect();
                return;
            }
        }

        public event EventHandler<TcpChannelEventArgs> ConnectingEvent;
        public event EventHandler<TcpChannelConnectedEventArgs> ConnectedEvent;
        public event EventHandler<TcpChannelEventArgs> DisConnectEvent;
        public event EventHandler<TcpChannelReceivedEventArgs> ReceivedEvent;
        public event EventHandler<TcpChannelErrorEventArgs> ErrorEvent;

        /// <summary>
        /// 为 this.ClientSocket 绑定新建立连接时的触发事件
        /// </summary>
        protected virtual void OnConnecting()
        {
            if (ConnectingEvent != null)
            {
                ConnectingEvent.BeginInvoke(this, new TcpChannelEventArgs() { sock = ClientSocket }, new AsyncCallback(ConnectingAsyncResult), ConnectingEvent);
            }
        }

        /// <summary>
        /// 新建立时连接异步回调
        /// </summary>
        /// <param name="e"></param>
        protected virtual void ConnectingAsyncResult(IAsyncResult e)
        {
            var messageOut = e.AsyncState as EventHandler<TcpChannelEventArgs>;
            if (messageOut != null)
            {
                messageOut.EndInvoke(e);
            }
        }

        /// <summary>
        /// 为 this.ClientSocket 绑定连接成功时的触发事件
        /// </summary>
        /// <param name="flag"></param>
        protected virtual void OnConnected(bool flag)
        {
            if (ConnectedEvent != null)
            {
                ConnectedEvent.BeginInvoke(this, new TcpChannelConnectedEventArgs() { sock = ClientSocket, ConnectFlag = flag }, new AsyncCallback(ConnectedAsyncResult), ConnectedEvent);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="ar"></param>
        protected virtual void ConnectedAsyncResult(IAsyncResult ar)
        {
            var messageOut = ar.AsyncState as EventHandler<TcpChannelConnectedEventArgs>;
            if (messageOut != null)
            {
                try
                {
                    messageOut.EndInvoke(ar);
                }
                catch (Exception ex)
                {
                    onError("ConnectedCallBack错误：", ex);
                }
            }
        }

        protected virtual void OnDisconnect()
        {
            if (DisConnectEvent != null)
            {
                DisConnectEvent.BeginInvoke(this, new TcpChannelEventArgs() { sock = ClientSocket }, new AsyncCallback(DisConnectEventCallBack), DisConnectEvent);
            }
        }

        protected virtual void DisConnectEventCallBack(IAsyncResult ar)
        {
            EventHandler<TcpChannelEventArgs> MessageOut = ar.AsyncState as EventHandler<TcpChannelEventArgs>;
            if (MessageOut != null)
            {
                try
                {
                    MessageOut.EndInvoke(ar);
                }
                catch (Exception ex)
                {
                    onError("DisConnectedCallBack错误：", ex);
                }
            }
        }

        protected virtual void onReceived(byte[] bytes)
        {
            if (ReceivedEvent != null)
            {
                //ReceivedEvent.BeginInvoke(this, new TcpChannelReceivedEventArgs() { session = ClientSocket, bytes = bytes }, new AsyncCallback(ReceivedEventCallBack), ReceivedEvent);
            }
        }

        //private async Task FillPipeAsync(PipeWriter writer, CancellationToken token = default)
        //{
        //    try
        //    {
        //        while (!token.IsCancellationRequested)
        //        {
        //            //if (token.IsCancellationRequested)
        //            //{
        //            //    return;
        //            //}

        //            try
        //            {
        //                Memory<byte> memory = writer.GetMemory(_maxWriteBufferSize);

        //                //设备多久没发数据就断开连接 Receive Timeout.
        //                int bytesRead = await ClientSocket.ReceiveAsync(memory, SocketFlags.None, _tcpSession.ReceiveTimeout.Token);
        //                if (bytesRead == 0)
        //                {
        //                    break;
        //                }
        //                writer.Advance(bytesRead);// 告诉PipeWriter从套接字读取了多少
        //            }
        //            catch (SocketException ex)
        //            {

        //                _trace.LogError($"{ex.Message}[{ex.SocketErrorCode}]:{_tcpSession.Client.RemoteEndPoint}");

        //                break;
        //            }
        //            catch (ObjectDisposedException)
        //            {

        //                _trace.LogError("[Receive Timeout]");

        //                break;
        //            }
        //            catch (OperationCanceledException)
        //            {

        //                _trace.LogError($"[Receive Timeout]:{_tcpSession.Client.RemoteEndPoint}");

        //                break;
        //            }
        //            catch (Exception)
        //            {

        //                _trace.LogError($"[Receive Error]:{_tcpSession.Client.RemoteEndPoint}");

        //                break;
        //            }

        //            FlushResult result = await writer.FlushAsync(token);// 标记数据可用，让PipeReader读取
        //            if (result.IsCompleted)
        //            {
        //                break;
        //            }
        //        }
        //    }
        //    finally
        //    {
        //        await writer.CompleteAsync();// 告诉PipeReader没有更多的数据
        //    }
        //}

        protected virtual void ReceivedEventCallBack(IAsyncResult ar)
        {
            EventHandler<TcpChannelReceivedEventArgs> MessageOut = ar.AsyncState as EventHandler<TcpChannelReceivedEventArgs>;
            if (MessageOut != null)
            {
                try
                {
                    MessageOut.EndInvoke(ar);
                }
                catch (Exception ex)
                {
                    onError("ReceiveCallBack错误：", ex);
                }
            }
        }

        protected virtual void onError(string content, Exception ex)
        {
            if (ErrorEvent != null)
            {
                ErrorEvent.BeginInvoke(this, new TcpChannelErrorEventArgs() { sock = ClientSocket, content = content, ex = ex }, new AsyncCallback(ErrorEventCallBack), ErrorEvent);
            }
        }

        protected virtual void ErrorEventCallBack(IAsyncResult ar)
        {
            EventHandler<TcpChannelErrorEventArgs> MessageOut = ar.AsyncState as EventHandler<TcpChannelErrorEventArgs>;
            if (MessageOut != null)
            {
                try
                {
                    MessageOut.EndInvoke(ar);
                }
                catch { }
            }
        }

        //protected void Close() {
        //    if (ClientSocket != null)
        //    {
        //        try
        //        {
        //            ClientSocket.Shutdown(SocketShutdown.Both);
        //        }
        //        catch { }
        //        finally
        //        {
        //            ClientSocket.Close();
        //            ClientSocket.Dispose();
        //        }
        //    }
        //}
        #region IDisposable

        private bool disposedValue;
        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)
                    if (ClientSocket != null)
                    {
                        try
                        {
                            ClientSocket.Shutdown(SocketShutdown.Both);
                        }
                        catch { }
                        finally
                        {
                            ClientSocket.Close();
                            ClientSocket.Dispose();
                        }
                    }
                }

                ClientSocket = null;
                Buffer = null;
                ConnectingEvent = null;
                ConnectedEvent = null;
                DisConnectEvent = null;
                ReceivedEvent = null;
                ErrorEvent = null;
                // TODO: 释放未托管的资源(未托管的对象)并替代终结器
                // TODO: 将大型字段设置为 null
                disposedValue = true;
            }
        }

        // // TODO: 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器
        // ~TcpChannel()
        // {
        //     // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
        //     Dispose(disposing: false);
        // }

        public void Dispose()
        {
            // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }
        #endregion

        //public virtual void Dispose()
        //{
        //    ConnectingEvent = null;
        //    ConnectedEvent = null;
        //    DisConnectEvent = null;
        //    ReceivedEvent = null;
        //    ErrorEvent = null;

        //    if (this.ClientSocket != null)
        //    {
        //        this.ClientSocket.Close();
        //        this.ClientSocket.Dispose();
        //        this.ClientSocket = null;
        //    }
        //        this.Buffer = null;
        //}
    }
}

